use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use redis::Client;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use validator::{Validate, ValidationError};

use crate::{
    error::ServerError,
    middleware::{
        jwt::{Claims, JWT_TTL},
        validator::ValidatedJson,
    },
    util::datetime::current_timestamp,
};

#[derive(Deserialize)]
pub struct LoginRequest {
    username: String,
    password: String,
}

#[derive(Serialize)]
pub struct LoginResponse {
    token: String,
}

pub async fn login_handler(
    Json(request): Json<LoginRequest>,
) -> Result<Json<LoginResponse>, ServerError> {
    let (username, password) = (request.username, request.password);
    if let Ok(user_id) = login_check(username.as_str(), password.as_str()).await {
        let claims = Claims::new(user_id);
        Ok(Json(LoginResponse {
            token: claims.get_jwt(),
        }))
    } else {
        Err(ServerError::Authentication)
    }
}

/// todo: check username and password and query uuid from db
async fn login_check(_username: &str, _password: &str) -> Result<Uuid, ServerError> {
    Ok(Uuid::new_v4())
}

/// 退出登录
/// 将jwt置为无效状态
pub async fn logout_handler(claims: Claims) -> Result<Response, ServerError> {
    let current = current_timestamp();
    if claims.exp > current {
        let remaining_time = (claims.exp - current) / 1000;
        let client = Client::open("redis://127.0.0.1")?;
        let mut con = client.get_async_connection().await?;
        redis::cmd("SETEX")
            .arg(claims.get_jwt())
            .arg(remaining_time)
            .arg(1)
            .query_async(&mut con)
            .await?;
    }
    let body = Json(serde_json::json!({"msg": "注销登录成功"}));
    Ok((StatusCode::OK, body).into_response())
}

#[derive(Deserialize, Validate)]
pub struct PasswordModifyRequest {
    #[validate(custom = "password_validate")]
    password: String,
    // 验证码，示例代码未用到
    #[allow(dead_code)]
    verification_code: String,
}

fn password_validate(password: &String) -> Result<(), ValidationError> {
    // 自定义检查: 例如特殊字符检查
    let len = password.len();
    if len < 6 {
        return Err(ValidationError::new("密码最短为6!"));
    } else if len > 16 {
        return Err(ValidationError::new("密码最长为16!"));
    }
    Ok(())
}

/// 密码修改
/// 记录密码修改时间，密码修改时间之前的token全部失效
pub async fn password_modify_handler(
    claims: Claims,
    _request: ValidatedJson<PasswordModifyRequest>,
) -> Result<Response, ServerError> {
    // todo: database modufy password
    let client = Client::open("redis://127.0.0.1")?;
    let mut con = client.get_async_connection().await?;
    redis::cmd("SETEX")
        .arg(claims.user_id.to_string())
        .arg(JWT_TTL)
        .arg(current_timestamp())
        .query_async(&mut con)
        .await?;
    let body = Json(serde_json::json!({"msg": "密码修改成功"}));
    Ok((StatusCode::OK, body).into_response())
}
